package org.oep.jmingle;

import org.oep.bluetooth.BTHandler;
import org.oep.game.ImageStore;
import org.oep.widgets.ChoiceWidget;
import org.oep.widgets.MonologueWidget;
import java.io.IOException;
import java.util.Hashtable;
import java.util.Vector;

import javax.microedition.lcdui.game.GameCanvas;
import javax.microedition.lcdui.game.Sprite;
import javax.microedition.lcdui.game.TiledLayer;
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.Image;

/**
 * This is a small tutorial for the user. Really, there's a lot of reused code from class Field
 * so there may be a way to combine the two one day. For now, you just got this.
 * @author pmkilgo
 *
 */
public class TutorialScreen extends GameCanvas implements Runnable {
	private Minglet player;
	private Minglet guest;
	
	private ChoiceWidget menu;
	
	private Sprite scanningIcon = null;
	private Sprite arrowSprite = null;
	
	private Thread gameThread;
	private MonologueWidget monoBox;
	private EventPipe eventHandler;
	
	private boolean playerSaysName = false;
	private boolean guestSaysName = false;
	private boolean playerSmiles = false;
	private boolean guestSmiles = false;
	private boolean guestIsFalling = false;
	private boolean showArrow = false;
	
	private int fallVelocity = 0;
	private int fallLine = 0;
	private int arrowPhase = 0;
	
	private boolean scanning = false;
	private boolean offlineMode = false;
	private boolean running = true;
	private boolean waiting = false;
	
	private Vector scripting;
	private int scriptPosition = 0;
	
	private BTHandler bt = new BTHandler();
	
	private TiledLayer background;
	private int Y_MIN;
	
	public TutorialScreen() {
		super(false);
		construct();
		toInitialState();
	}

	public void run() {
		while(running) {
			doLogic();
			checkInput();
			updateGraphics(getGraphics());
			
			try {
				Thread.sleep(83);
			} catch(Exception e) {
				
			}
		}
	}
	
	private void doLogic() {
		parseScript();
		
		player.nextFrame();
		player.doAI();
		
		if(guestIsFalling) {
			fallVelocity++;
			fallLine = Math.max(0, fallLine - fallVelocity);
			
			if(fallLine == 0 && guest.getFrame() < guest.getFrameSequenceLength() - 2) {
				guest.nextFrame();
			}
			else if(fallLine == 0) {
				guestIsFalling = false;
				guest.setAnimationState(Minglet.KEY_STAND);
			}
		} else {
			guest.doAI();
			guest.nextFrame();
		}
		
		monoBox.tick();
	}
	
	private void parseScript() {
		if(scriptPosition < 0 || scriptPosition >= scripting.size()) return;
		
		while(scriptPosition <= scripting.size() - 1 && !waiting) {
			parseCurrentLine();
			scriptPosition++;
		}
		
	}
	
	private void parseCurrentLine() {
		String line = (String) scripting.elementAt(scriptPosition);
		String [] tokens = Utils.split(line, ' ');
		
		if(tokens.length < 1) return;
		
		String command = tokens[0].toLowerCase();
		
		if(command.compareTo("display") == 0) {
			String s = new String();
			for(int i = 1; i < tokens.length; i++) {
				s += tokens[i] + " ";
			}
			
			s = Utils.replaceAllIgnoreCase(s, "%name%", player.getName()); 
			
			if(s.length() > 0) {
				scriptMsg("Displaying: " + s);
				monoBox.display(s);
				monoBox.setActive(true);
			}
			else {
				scriptMsg("I refuse to display an empty string");
			}
		}
		else if(command.compareTo("wait") == 0) {
			scriptMsg("Waiting...");
			waiting = true;
		}
		else if(command.compareTo("goto") == 0) {
			Minglet dood = null;
			
			if(tokens.length >= 2) {
				String name = tokens[1].toLowerCase();
				
				if(name.compareTo("player") == 0) {
					scriptMsg("Selecting the player");
					dood = player;
				}
				else if(name.compareTo("guest") == 0) {
					scriptMsg("Selecting the guest");
					dood = guest;
				}
				else {
					scriptErr(name + ": I do not know this minglet");
					return;
				}
			}
			
			if(tokens.length == 3) {
				String arg = tokens[2].toLowerCase();
				if(arg.compareTo("center") == 0) {
					scriptMsg(dood + " is going to the center");
					dood.goTo(getWidth() / 2, Y_MIN + getHeight() / 2);
				}
				else if(arg.compareTo("guest") == 0) {
					scriptMsg(dood + " is going to guest");
					int x = guest.getX(); int y = guest.getY(); int dx = guest.getWidth() / 2;
					int m = getWidth() / 2;
					dood.goTo(x + dx * ((x > m) ? 1 : -1), y);
				}
				else {
					scriptErr(arg + ": Unknown keyword");
				}
			}
			else if(tokens.length == 4) {
				String x = tokens[2]; String y = tokens[3];
				scriptMsg(dood + " is going to " + x + ", " + y);
				dood.goTo(Integer.valueOf(x).intValue(), Integer.valueOf(y).intValue());
			}
			else {
				scriptErr("goto takes 3 and 4 arguements. You provided: " + tokens.length);
				return;
			}
		}
		else if(command.compareTo("mingle") == 0) {
			Minglet mingler = null;
			Minglet minglee = null;
			
			if(tokens.length == 3) {
				String first = tokens[1].toLowerCase();
				String second = tokens[2].toLowerCase();
				
				if(first.compareTo("player") == 0) {
					mingler = player;
				}
				else if(first.compareTo("guest") == 0) {
					mingler = guest;
				}
				else {
					scriptErr(first + ": I do not know this minglet");
					return;
				}
				
				if(second.compareTo("player") == 0) {
					minglee = player;
				}
				else if(second.compareTo("guest") == 0) {
					minglee = guest;
				}
				else {
					scriptErr(second + ": I do not know this minglet");
					return;
				}
			}
			else {
				scriptErr("goto takes 3 and 4 arguements. You provided: " + tokens.length);
				return;
			}
			
			mingler.setTarget(minglee);
		}
		else if(command.compareTo("anim") == 0) {
			if(tokens.length == 3) {
				String name = tokens[1].toLowerCase();
				String anim = tokens[2].toLowerCase();
				
				Minglet dood = null;
				
				if(name.compareTo("player") == 0) {
					dood = player;
				}
				else if(name.compareTo("guest") == 0) {
					dood = guest;
				}
				else {
					scriptErr(name + ": I do not know this minglet");
					return;
				}
				
				if(anim.compareTo("smiling") == 0) {
					if(dood == player) {
						playerSmiles = true;
					}
					else if(dood == guest) {
						guestSmiles = true;
					}
				}
				else if(anim.compareTo("normal") == 0) {
					dood.setAnimationState(Minglet.KEY_STAND);
					
					if(dood == player) {
						playerSmiles = false;
					}
					else if(dood == guest) {
						guestSmiles = false;
					}
					
				}
				else {
					scriptErr(anim + ": I do not know this animation");
					return;
				}
				
			}
			else {
				scriptErr("`smile' takes two arguements");
			}
		}
		else if(command.compareTo("scan") == 0) {
			if(tokens.length == 2) {
				String arg = tokens[1].toLowerCase();
				scanning = arg.compareTo("on") == 0;
			}
		}
		else if(command.compareTo("dropin") == 0) {
			guestIsFalling = true;
			fallLine = getHeight();
			guest.setAnimationState(Minglet.KEY_FALL);
			guest.setPosition(JMingle.RANDOM.nextInt(getWidth()), player.getY());
		}
		else if(command.compareTo("point") == 0) {
			if(tokens.length == 2) {
				String arg = tokens[1].toLowerCase();
				int x = 0, y = 0;
				if(arg.compareTo("player") == 0) {
					int px = player.getX(); int py = player.getY();
					int w = arrowSprite.getWidth();	int ph = player.getHeight();
					x = px - w / 2;
					y = py - ph * 7 / 4;
				}
				else if(arg.compareTo("off") == 0) {
					showArrow = false;
					return;
				}
				
				arrowSprite.setPosition(x, y);
				showArrow = true;
			}
		}
		else if(command.compareTo("menu") == 0) {
			if(tokens.length < 2) {
				scriptErr("`menu` takes at least two arguements");
				return;
			}
			
			String action = tokens[1].toLowerCase();
			
			if(action.compareTo("hide") == 0) {
				menu.setActive(false);
			}
			else if(action.compareTo("show") == 0 && tokens.length == 3) {
				menu.clear();
				int Mingle = menu.addChoice("Mingle");
				int Info = menu.addChoice("Info");
				int Scan = menu.addChoice("Scan");
				int Book = menu.addChoice("Minglebook");
				int Browse = menu.addChoice("Browse");
				int Quit = menu.addChoice("Quit");
				int Cancel = menu.addChoice("Cancel");
				menu.setActive(true);
				
				String item = tokens[2].toLowerCase();
				
				if(item.compareTo("scan") == 0) {
					menu.setChoice(Scan);
				}
				else if(item.compareTo("mingle") == 0) {
					menu.setChoice(Mingle);
				}
			}
		}
		else if(command.compareTo("leave") == 0 && tokens.length == 2) {
			String dude = tokens[1].toLowerCase();
			if(dude.compareTo("player") == 0) {
				player.interrupt();
				player.goTo(-player.getWidth(), player.getY());
			}
			else if(dude.compareTo("guest") == 0) {
				guest.interrupt();
				guest.goTo(-guest.getWidth(), guest.getY());
			}
			else {
				scriptErr("I don't know who " + dude + " is");
			}
		}
		else {
			scriptErr(command + ": unknown command");
		}
	}
	
	private void checkInput() {
		int keyStates = getKeyStates();
		if((keyStates & FIRE_PRESSED) != 0) {
			if(monoBox.isActive()) {
				monoBox.action();
				
				// See if the dialogue was inactivated and auto-progress
				if(!monoBox.isActive() && waiting) {
					waiting = false;
					parseScript();
				}
			}
			else if(waiting) {
				waiting = false;
			}
			else if(eventHandler != null) {
				eventHandler.eventReceived(JMingle.SHOW_MAIN);
				running = false;
			}
		}
	}
	
	private void updateGraphics(Graphics g) {
		g.setColor(0xFFFFFF);
		g.fillRect(0,0,getHeight(),getWidth());
		
		background.paint(g);
		
		if(playerSmiles) {
			player.setAnimationState(Minglet.KEY_SMILE);
		}
		
		if(guestSmiles) {
			guest.setAnimationState(Minglet.KEY_SMILE);
		}
		
		if(player.getY() < guest.getY())
			player.paint(g);
		
		if(guestIsFalling) {
			guest.paint(g, guest.getX(), guest.getY() - fallLine);
		}
		else {
			guest.paint(g);
		}
		
		if(player.getY() >= guest.getY())
			player.paint(g);
		
		if(scanning) {
			scanningIcon.paint(g);
			scanningIcon.nextFrame();
		}
		
		if(showArrow) {
			int x = arrowSprite.getX(), y = arrowSprite.getY();
			arrowPhase = (arrowPhase + 20) % 360;
			int dy = (int) (15 * Math.sin(arrowPhase * Math.PI / 180));
			arrowSprite.setPosition(x, y + dy);
			arrowSprite.paint(g);
			arrowSprite.setPosition(x, y);
		}
		
		if(monoBox.isActive())
			monoBox.paint(g);
		else
			monoBox.flushGraphics(this);
		
		menu.paint(g);
		
		flushGraphics();
	}

	public void construct() {
		String hash = bt.getMyMAC();
		String name = bt.getMyDeviceName();
		
		if(name == null || hash == null) {
			offlineMode = true;
			hash = "0123456789ABCDEF";
		}
		
		scripting = Utils.getLines("/res/text/tut-script.txt");
		System.out.println("Found " + scripting.size() + " lines");
		
		try {
			Image tmp = ImageStore.getInstance().get("/res/graphics/loading.png");
			scanningIcon = new Sprite(tmp, 18, 18);
			scanningIcon.setPosition(getWidth() / 2 - scanningIcon.getWidth() / 2, getHeight() / 2 - scanningIcon.getHeight() / 2);
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		
		try {
			Image arrowImage = ImageStore.getInstance().get("/res/graphics/arrow-down.png");
			arrowSprite = new Sprite(arrowImage, arrowImage.getWidth(), arrowImage.getHeight());
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		
		buildBackground();
		player = Utils.makeMinglet(hash, name);
		guest = Utils.makeMinglet(Integer.toHexString(JMingle.RANDOM.nextInt()), "JMingle");
		monoBox = new MonologueWidget(getWidth()/16, 10, (14 * getWidth()) / 16, 3, 3);
		monoBox.setFont(JMingle.DEFAULT_FONT);
		
		menu = new ChoiceWidget(ChoiceWidget.TYPE_BAR);
		menu.setPosition(getWidth() / 2, menu.getHeight());
		
		monoBox.setPosition(getWidth() / 2 - monoBox.getWidth() / 2, menu.getHeight() + 3);
	}
	
	private void buildBackground( ) {
		int width = getWidth();
		int height = getHeight();
		try {
			Image im = Image.createImage("/res/graphics/bg-tiles.png");
			
			int	xTiles = 1 + (int) Math.ceil( width / 16 );
			int	yTiles = 1 + (int) Math.ceil( height / 16 );
			background = new TiledLayer(xTiles, yTiles, im, 16, 16);
			
			Y_MIN = (16 * yTiles) - (4 * 16);
			
			for(int i = 0; i < yTiles; i++) {
				for(int j = 0; j < xTiles; j++) {
					// Grass
					if(i > yTiles - 4)
						background.setCell(j, i, 7);
					// Grass tips
					else if(i == yTiles - 4)
						background.setCell(j, i, 6);
					// Sky
					else
						background.setCell(j, i, 5);
				}
			}
			
			// Sun
			background.setCell(xTiles - 3, 1, 1);
			background.setCell(xTiles - 2, 1, 2);
			background.setCell(xTiles - 3, 2, 3);
			background.setCell(xTiles - 2, 2, 4);
		
		}
		catch(IOException e) {
			
		}
	}
	
	public void setEventPipe(EventPipe e) {
		eventHandler = e;
	}
	
	public void toInitialState() {
		player.setPosition(getWidth() / 2, (Y_MIN + getHeight()) / 2);
		guest.setPosition(-guest.getWidth(), (Y_MIN + getHeight()) / 2);
		scriptPosition = 0;
		monoBox.clearBuffer();
	}
	
	public void pause() {
		if(gameThread != null && gameThread.isAlive()) {
			gameThread.interrupt();
		}
	}
	
	public void start() {
		if(gameThread == null || !gameThread.isAlive()) {
			gameThread = new Thread(this);
			gameThread.start();
		}
		running = true;
	}
	
	private void scriptMsg(String msg) {
		System.out.println("Script message: " + msg);
	}
	
	private void scriptErr(String msg) {
		System.err.println("Script error: " + msg);
	}
}
